#include "DefaultState.hpp"
#include "DialogState.hpp"
#include "../Notification.hpp"
#include "../AssetManager.hpp"
#include "../Util.hpp"
#include <TweenEngine/Tween.h>
#include <cpp3ds/System/I18n.hpp>
#include <cpp3ds/Window/Window.hpp>
#ifndef EMULATION
#include "../Nvram.hpp"
#include "../KeyboardApplet.hpp"
#endif

namespace AdvDSProfile {

DefaultState *g_defaultState = nullptr;

DefaultState::DefaultState(StateStack& stack, Context& context, StateCallback callback)
: State(stack, context, callback)
, m_isDefaultMode(true)
{
	// State constructor, initialize everything here
	g_defaultState = this;

	cpp3ds::Color themeColors[] = {cpp3ds::Color(97, 130, 154), cpp3ds::Color(186, 73, 0), cpp3ds::Color(251, 0, 24), cpp3ds::Color(251, 138, 251), cpp3ds::Color(251, 146, 0), cpp3ds::Color(243, 227, 0), cpp3ds::Color(170, 251, 0), cpp3ds::Color(0, 251, 0), cpp3ds::Color(0, 162, 56), cpp3ds::Color(73, 219, 138), cpp3ds::Color(48, 186, 243), cpp3ds::Color(0, 89, 243), cpp3ds::Color(0, 0, 146), cpp3ds::Color(138, 0, 211), cpp3ds::Color(211, 0, 235), cpp3ds::Color(251, 0, 146)};

	m_textUsername.setFillColor(cpp3ds::Color::Black);
	m_textUsername.setCharacterSize(24);
	m_textUsername.useSystemFont();
	m_textUsername.setPosition(200.f, 70.f);
	updateUsernameText();

	m_textMessage = m_textUsername;
	m_textMessage.setCharacterSize(16);
	m_textMessage.setPosition(200.f, 140.f);
	m_textMessage.setFillColor(cpp3ds::Color(33, 33, 33));
	updateMessageText();

	m_textBirthday = m_textMessage;
	m_textBirthday.setFillColor(cpp3ds::Color(99, 99, 99));
	m_textBirthday.setPosition(395.f, 235.f);
	updateBirthdayText();

	m_buttonUsername.setFillColor(cpp3ds::Color(245, 245, 245));
	m_buttonUsername.setOutlineColor(cpp3ds::Color(158, 158, 158));
	m_buttonUsername.setOutlineThickness(1);
	m_buttonUsername.setPosition(35.f, 55.f);
	m_buttonUsername.setSize(cpp3ds::Vector2f(120.f, 60.f));

	m_textUsernameBtn.setFillColor(cpp3ds::Color::Black);
	m_textUsernameBtn.setString("Change username");
	m_textUsernameBtn.setCharacterSize(12);
	m_textUsernameBtn.setPosition(m_buttonUsername.getPosition().x + (m_buttonUsername.getLocalBounds().width / 2), m_buttonUsername.getPosition().y + (m_buttonUsername.getLocalBounds().height / 2));
	m_textUsernameBtn.useSystemFont();
	m_textUsernameBtn.setOrigin(m_textUsernameBtn.getLocalBounds().width / 2, m_textUsernameBtn.getLocalBounds().height / 2);

	m_buttonMessage = m_buttonUsername;
	m_buttonMessage.setPosition(163.f, 55.f);

	m_textMessageBtn = m_textUsernameBtn;
	m_textMessageBtn.setString("Change message");
	m_textMessageBtn.setOrigin(m_textMessageBtn.getLocalBounds().width / 2, m_textMessageBtn.getLocalBounds().height / 2);
	m_textMessageBtn.setPosition(m_buttonMessage.getPosition().x + (m_buttonMessage.getLocalBounds().width / 2), m_buttonMessage.getPosition().y + (m_buttonMessage.getLocalBounds().height / 2));

	m_buttonBdayDay = m_buttonUsername;
	m_buttonBdayDay.setPosition(35.f, 123.f);

	m_textBdayDayBtn = m_textUsernameBtn;
	m_textBdayDayBtn.setString("Change birthday\nday");
	m_textBdayDayBtn.setOrigin(m_textBdayDayBtn.getLocalBounds().width / 2, m_textBdayDayBtn.getLocalBounds().height / 2);
	m_textBdayDayBtn.setPosition(m_buttonBdayDay.getPosition().x + (m_buttonBdayDay.getLocalBounds().width / 2), m_buttonBdayDay.getPosition().y + (m_buttonBdayDay.getLocalBounds().height / 2));

	m_buttonBdayMonth = m_buttonUsername;
	m_buttonBdayMonth.setPosition(163.f, 123.f);

	m_textBdayMonthBtn = m_textUsernameBtn;
	m_textBdayMonthBtn.setString("Change birthday\nmonth");
	m_textBdayMonthBtn.setOrigin(m_textBdayMonthBtn.getLocalBounds().width / 2, m_textBdayMonthBtn.getLocalBounds().height / 2);
	m_textBdayMonthBtn.setPosition(m_buttonBdayMonth.getPosition().x + (m_buttonBdayMonth.getLocalBounds().width / 2), m_buttonBdayMonth.getPosition().y + (m_buttonBdayMonth.getLocalBounds().height / 2));

	m_textChangeMode.setFillColor(cpp3ds::Color::White);
	m_textChangeMode.setOutlineColor(cpp3ds::Color::Black);
	m_textChangeMode.setOutlineThickness(1);
	m_textChangeMode.setString("\uf060");
	m_textChangeMode.setFont(AssetManager<cpp3ds::Font>::get("fonts/fontawesome.ttf"));
	m_textChangeMode.setCharacterSize(20);
	m_textChangeMode.setPosition(300.f, 215.f);
	m_textChangeMode.setOrigin(m_textChangeMode.getLocalBounds().width / 2, m_textChangeMode.getLocalBounds().height / 2);

	m_textPickColor = m_textUsername;
	m_textPickColor.setString("Pick a color");
	m_textPickColor.setCharacterSize(18);
	m_textPickColor.setPosition(160.f + 320.f, 50.f);
	m_textPickColor.setOrigin(m_textPickColor.getLocalBounds().width / 2, m_textPickColor.getLocalBounds().height / 2);

	m_separator.setFillColor(cpp3ds::Color(158, 158, 158));
	m_separator.setSize(cpp3ds::Vector2f(300.f, 1.f));
	m_separator.setOrigin(m_separator.getLocalBounds().width / 2, 0.f);
	m_separator.setPosition(200.f, 90.f);

	m_textAdvDSProfile = m_textPickColor;
	m_textAdvDSProfile.setString("Advanced DS Profile");
	m_textAdvDSProfile.setPosition(160.f, 30.f);
	m_textAdvDSProfile.setOrigin(m_textAdvDSProfile.getLocalBounds().width / 2, m_textAdvDSProfile.getLocalBounds().height / 2);

	float posX = 20.f;
	for (int i = 0; i < 16; ++i) {
		if (i % 8 == 0)
			posX = 20.f;

		std::shared_ptr<util3ds::TweenRectangleShape> item(new util3ds::TweenRectangleShape());
		m_colorBlocks.emplace_back(std::move(item));
		util3ds::TweenRectangleShape *lastBlockPointer = m_colorBlocks.back().get();

		lastBlockPointer->setFillColor(cpp3ds::Color(themeColors[i].r, themeColors[i].g, themeColors[i].b, 128));
		lastBlockPointer->setOutlineColor(themeColors[i]);
		lastBlockPointer->setOutlineThickness(1);
		lastBlockPointer->setPosition(posX + 320.f, 100.f + 60.f * ceil(i / 8));
		lastBlockPointer->setSize(cpp3ds::Vector2f(20.f, 20.f));

		posX += 37.f;
	}

	updateColors();
}

DefaultState::~DefaultState()
{

}

void DefaultState::renderTopScreen(cpp3ds::Window& window)
{
	window.draw(m_textUsername);
	window.draw(m_textMessage);
	window.draw(m_textBirthday);
	window.draw(m_separator);

	window.draw(m_topInfos);
}

void DefaultState::renderBottomScreen(cpp3ds::Window& window)
{
	window.draw(m_buttonUsername);
	window.draw(m_textUsernameBtn);
	window.draw(m_buttonMessage);
	window.draw(m_textMessageBtn);
	window.draw(m_buttonBdayDay);
	window.draw(m_textBdayDayBtn);
	window.draw(m_buttonBdayMonth);
	window.draw(m_textBdayMonthBtn);

	window.draw(m_textChangeMode);
	window.draw(m_textAdvDSProfile);

	for (auto& item : m_colorBlocks) {
		window.draw(*item);
	}
	window.draw(m_textPickColor);
}

bool DefaultState::update(float delta)
{
	// Always update the tween manager if you have one in your class
	m_tweenManager.update(delta);

	m_topInfos.update(delta);

	return true;
}

bool DefaultState::processEvent(const cpp3ds::Event& event)
{
	if (event.type == cpp3ds::Event::TouchEnded) {
		if (m_buttonUsername.getGlobalBounds().contains(event.touch.x, event.touch.y) && m_isDefaultMode) {
#ifndef EMULATION
				KeyboardApplet kb(KeyboardApplet::Text, 10);
				swkbdSetFeatures(kb, SWKBD_FIXED_WIDTH);
				cpp3ds::String input = kb.getInput();
				if (!input.isEmpty())
					NVRAM::getInstance().writeUsername(input);

				updateUsernameText();
#endif
		} else if (m_buttonMessage.getGlobalBounds().contains(event.touch.x, event.touch.y) && m_isDefaultMode) {
#ifndef EMULATION
				KeyboardApplet kb(KeyboardApplet::Text, 26);
				swkbdSetFeatures(kb, SWKBD_FIXED_WIDTH);
				cpp3ds::String input = kb.getInput();
				if (!input.isEmpty())
					NVRAM::getInstance().writeMessage(input);

				updateMessageText();
#endif
		} else if (m_buttonBdayDay.getGlobalBounds().contains(event.touch.x, event.touch.y) && m_isDefaultMode) {
#ifndef EMULATION
				KeyboardApplet kb(KeyboardApplet::Number, 2);
				swkbdSetFeatures(kb, SWKBD_FIXED_WIDTH);
				cpp3ds::String input = kb.getInput();
				if (!input.isEmpty()) {
					int inputNbr = stoi(input.toAnsiString());
					int birthDay = 0;
					int birthMonth = 0;
					NVRAM::getInstance().readBirthday(&birthDay, &birthMonth);
					int maxDay = getDaysInMonth(birthMonth);

					if (inputNbr >= 1 && inputNbr <= maxDay) {
						NVRAM::getInstance().writeBirthdayDay(inputNbr);
					} else {
						requestStackPush(States::Dialog, false, [=](void *data) mutable {
							auto event = reinterpret_cast<DialogState::Event*>(data);
							if (event->type == DialogState::GetText)
							{
								auto str = reinterpret_cast<cpp3ds::String*>(event->data);
								*str = _("The requested day (%i) is out of range (1 - %i), if you continue, some game that are using these values may crash.\n\nDo you want to continue ?", inputNbr, maxDay);
								return true;
							}
							else if (event->type == DialogState::GetTitle) {
								auto str = reinterpret_cast<cpp3ds::String *>(event->data);
								*str = _("Birthday");
								return true;
							}
							else if (event->type == DialogState::Response)
							{
								bool *accepted = reinterpret_cast<bool*>(event->data);
								if (*accepted)
								{
									NVRAM::getInstance().writeBirthdayDay(inputNbr);
									updateBirthdayText();
								}
								return true;
							}
							return false;
						});
					}
				}

				updateBirthdayText();
#endif
		} else if (m_buttonBdayMonth.getGlobalBounds().contains(event.touch.x, event.touch.y) && m_isDefaultMode) {
#ifndef EMULATION
				KeyboardApplet kb(KeyboardApplet::Number, 2);
				swkbdSetFeatures(kb, SWKBD_FIXED_WIDTH);
				cpp3ds::String input = kb.getInput();
				if (!input.isEmpty()) {
					int inputNbr = stoi(input.toAnsiString());

					if (inputNbr >= 1 && inputNbr <= 12) {
						NVRAM::getInstance().writeBirthdayMonth(inputNbr);
					} else {
						requestStackPush(States::Dialog, false, [=](void *data) mutable {
							auto event = reinterpret_cast<DialogState::Event*>(data);
							if (event->type == DialogState::GetText)
							{
								auto str = reinterpret_cast<cpp3ds::String*>(event->data);
								*str = _("The requested month (%i) is out of range (1 - 12), if you continue, some game that are using these values may crash.\n\nDo you want to continue ?", inputNbr);
								return true;
							}
							else if (event->type == DialogState::GetTitle) {
								auto str = reinterpret_cast<cpp3ds::String *>(event->data);
								*str = _("Birthday");
								return true;
							}
							else if (event->type == DialogState::Response)
							{
								bool *accepted = reinterpret_cast<bool*>(event->data);
								if (*accepted)
								{
									NVRAM::getInstance().writeBirthdayMonth(inputNbr);
									updateBirthdayText();
								}
								return true;
							}
							return false;
						});
					}
				}

				int birthDay = 0;
				int birthMonth = 0;
				NVRAM::getInstance().readBirthday(&birthDay, &birthMonth);
				int maxDay = getDaysInMonth(birthMonth);
				if (birthDay > maxDay)
					NVRAM::getInstance().writeBirthdayDay(maxDay);

				updateBirthdayText();
#endif
		} else if (m_textChangeMode.getGlobalBounds().contains(event.touch.x, event.touch.y)) {
			changeMode(!m_isDefaultMode);
		} else if (!m_isDefaultMode) {
			int i = 0;
			for (auto& item : m_colorBlocks) {
				if (item->getGlobalBounds().contains(event.touch.x, event.touch.y)) {
#ifndef EMULATION
					NVRAM::getInstance().writeTheme(i);
					updateColors();
#endif
					break;
				}
				i++;
			}
		}
	}

	return true;
}

void DefaultState::updateUsernameText()
{
	cpp3ds::String dsiUsername = "N/A";
#ifndef EMULATION
	NVRAM::getInstance().readUsername(&dsiUsername);
#endif

	m_textUsername.setString(dsiUsername);
	m_textUsername.setOrigin(m_textUsername.getLocalBounds().width / 2, m_textUsername.getLocalBounds().height / 2);
}

void DefaultState::updateMessageText()
{
	cpp3ds::String dsiMessage = "N/A";
#ifndef EMULATION
	NVRAM::getInstance().readMessage(&dsiMessage);
#endif

	m_textMessage.setString(dsiMessage);
	m_textMessage.setOrigin(m_textMessage.getLocalBounds().width / 2, m_textMessage.getLocalBounds().height / 2);
}

#define TWEEN_IN(obj) \
	TweenEngine::Tween::to(obj, obj.FILL_COLOR_ALPHA, 0.4f).target(255.f).delay(0.2f).start(m_tweenManager); \
	TweenEngine::Tween::to(obj, obj.OUTLINE_COLOR_ALPHA, 0.4f).target(255.f).delay(0.2f).start(m_tweenManager);

#define TWEEN_OUT(obj) \
	TweenEngine::Tween::to(obj, obj.FILL_COLOR_ALPHA, 0.4f).target(0.f).start(m_tweenManager); \
	TweenEngine::Tween::to(obj, obj.OUTLINE_COLOR_ALPHA, 0.4f).target(0.f).start(m_tweenManager);

void DefaultState::changeMode(bool defaultMode)
{
	m_isDefaultMode = defaultMode;

	if (defaultMode) {
		TWEEN_IN(m_buttonUsername);
		TWEEN_IN(m_textUsernameBtn);
		TWEEN_IN(m_buttonMessage);
		TWEEN_IN(m_textMessageBtn);
		TWEEN_IN(m_buttonBdayDay);
		TWEEN_IN(m_textBdayDayBtn);
		TWEEN_IN(m_buttonBdayMonth);
		TWEEN_IN(m_textBdayMonthBtn);
		TWEEN_IN(m_textAdvDSProfile);

		TweenEngine::Tween::to(m_textChangeMode, m_textChangeMode.ROTATION, 0.4f)
			.target(0.f)
			.start(m_tweenManager);

		TweenEngine::Tween::to(m_textChangeMode, m_textChangeMode.POSITION_X, 0.4f)
			.target(300.f)
			.start(m_tweenManager);

		for (auto& item : m_colorBlocks) {
			TweenEngine::Tween::to(*item, util3ds::TweenRectangleShape::POSITION_X, 0.2f)
				.target(item->getPosition().x + 320.f)
				.start(m_tweenManager);
		}

		TweenEngine::Tween::to(m_textPickColor, m_textPickColor.POSITION_X, 0.2f)
			.target(m_textPickColor.getPosition().x + 320.f)
			.start(m_tweenManager);
	} else {
		TWEEN_OUT(m_buttonUsername);
		TWEEN_OUT(m_textUsernameBtn);
		TWEEN_OUT(m_buttonMessage);
		TWEEN_OUT(m_textMessageBtn);
		TWEEN_OUT(m_buttonBdayDay);
		TWEEN_OUT(m_textBdayDayBtn);
		TWEEN_OUT(m_buttonBdayMonth);
		TWEEN_OUT(m_textBdayMonthBtn);
		TWEEN_OUT(m_textAdvDSProfile);

		TweenEngine::Tween::to(m_textChangeMode, m_textChangeMode.ROTATION, 0.4f)
			.target(180.f)
			.start(m_tweenManager);

		TweenEngine::Tween::to(m_textChangeMode, m_textChangeMode.POSITION_X, 0.4f)
			.target(20.f)
			.start(m_tweenManager);

		for (auto& item : m_colorBlocks) {
			TweenEngine::Tween::to(*item, util3ds::TweenRectangleShape::POSITION_X, 0.2f)
				.target(item->getPosition().x - 320.f)
				.delay(0.4f)
				.start(m_tweenManager);
		}

		TweenEngine::Tween::to(m_textPickColor, m_textPickColor.POSITION_X, 0.3f)
			.target(m_textPickColor.getPosition().x - 320.f)
			.delay(0.5f)
			.start(m_tweenManager);
	}
}

void DefaultState::updateColors()
{
#ifndef EMULATION
	int index;
	NVRAM::getInstance().readTheme(&m_colorTheme, &index);

	m_textUsername.setFillColor(m_colorTheme);
	m_textPickColor.setFillColor(m_colorTheme);
#endif
}

void DefaultState::updateBirthdayText()
{
	int birthDay = 0;
	int birthMonth = 0;
#ifndef EMULATION
	NVRAM::getInstance().readBirthday(&birthDay, &birthMonth);
#endif

	m_textBirthday.setString(_("Birthday %02d/%02d", birthDay, birthMonth));
	m_textBirthday.setOrigin(m_textBirthday.getLocalBounds().width, m_textBirthday.getLocalBounds().height);
}

} // namespace AdvDSProfile
